package org.activequant.dao.hibernate;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.activequant.core.domainmodel.marketscanner.ScanCriteria;
import org.activequant.dao.IScanCriteriaDao;
import org.activequant.util.exceptions.DaoException;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
/**
 * ScanCriteriaDao extends DaoSupportBase&lt;ScanCriteria&gt; implements IScanCriteriaDao.<br/>
 * Holds the following inherited variables:
 * <ul>
 * <li>clazz(Class&lt;ScanCriteria&gt;)</li>
 * <li>hibernateTemplate(HibernateTemplate)</li>
 * </ul>
 * @author Dimitar
 *
 */
public class ScanCriteriaDao extends DaoSupportBase<ScanCriteria> implements IScanCriteriaDao {
	/**
	 * Constructs a ScanCriteriaDao(extends DaoSupportBase&lt;ScanCriteria&gt; implements IScanCriteriaDao) whose inherited clazz(Class&lt;T&gt;) is set to <code>ScanCriteria.class</code>.
	 */
	public ScanCriteriaDao() {
		super(ScanCriteria.class);
	}
	/**
	 * Constructs a ScanCriteriaDao(extends DaoSupportBase&lt;ScanCriteria&gt; implements IScanCriteriaDao) whose inherited clazz(Class&lt;T&gt;) is set to <code>ScanCriteria.class</code>.<br/>
	 * The given sessionFactory(SessionFactory) is used to set its Hibernate SessionFactory and create/initialize the inherited hibernateTemplate(HibernateTemplate) for it.
	 * @param sessionFactory
	 */
	public ScanCriteriaDao(SessionFactory sessionFactory) {
		super(ScanCriteria.class, sessionFactory);
	}
	/**
	 * <strong>1.</strong> Deletes all persistent instances of the {MarketScan.class} whose ScanCriteria matches one of the given entities(List&lt;ScanCriteria&gt;)<br/>
	 * <strong>2.</strong> Deletes the given entities(List&lt;ScanCriteria&gt;) instances themselves.
	 */
	@Override
    public void delete(List<ScanCriteria> entities) throws DaoException {
		try {		
			HqlQueries.deleteScanCriteriaEntities(getHibernateTemplate(), entities.toArray(new ScanCriteria[] {}));
			getHibernateTemplate().deleteAll(new ArrayList<ScanCriteria>(entities));
		} catch (DataAccessException e) {
			throw new DaoException(e);
		}   	
    }
	/**
	 * <strong>1.</strong> Deletes all persistent instances of the {MarketScan.class} whose ScanCriteria matches any of the existing ScanCriteria instances<br/>
	 * <strong>2.</strong> Deletes all ScanCriteria instances.
	 */
	@Override @SuppressWarnings("unchecked")
	public void deleteAll() {
		try {
			List<ScanCriteria> specs = getHibernateTemplate().loadAll(clazz);
			// hibernate does not support orphan-delete on one-to-one or many-to-one
			HqlQueries.deleteScanCriteriaEntities(getHibernateTemplate(), specs.toArray(new ScanCriteria[] {}));
			getHibernateTemplate().deleteAll(specs);
		} catch (DataAccessException e) {
			throw new DaoException(e);
		}
	}
	/**
	 * Saves/updates and returns the given entity(ScanCriteria).<br/>
	 * If there was no id, it looks it up by finding an example.
	 */
	@Override
	public ScanCriteria update(ScanCriteria entity) {
		if(!entity.hasId()) {
			ScanCriteria storedSpec = lookupIfNeeded(entity);
			if(storedSpec != null) {
				entity.setId(storedSpec.getId());
				return storedSpec;
			}
		}
		return super.update(entity);
	}
	/**
	 * Returns the criterias(ScanCriteria[]) whose ScanCode matches the given scanCode(String)
	 */
	@SuppressWarnings("unchecked")
	public ScanCriteria[] findByScanCode(final String scanCode) throws DaoException {
		// TODO Auto-generated method stub
		return (ScanCriteria[])getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query query = session.createQuery("from " + ScanCriteria.class.getName() + " where scanCode='" + scanCode + "'");
				return query.list().toArray(new ScanCriteria[] {});
			}
		});
	}
	
	/**
	 * returns a String for the given criteria(ScanCriteria) in the form:<br/> 
	 * "from ScanCriteria where numberOfRows='${criteria.numberOfRows}' and  ....
	 * @param criteria
	 * @return
	 */
	public static String createScanCriteriaLookupQuery(ScanCriteria criteria){
		StringBuffer queryString=new StringBuffer("from " + ScanCriteria.class.getName() + " where numberOfRows='" + criteria.getNumberOfRows() + 	
		"' and abovePrice='" + criteria.getAbovePrice() + 
		"' and belowPrice='" + criteria.getBelowPrice() + 
		"' and aboveVolume='" + criteria.getAboveVolume() + 
		"' and averageOptionVolumeAbove='" + criteria.getAverageOptionVolumeAbove() + 
		"' and marketCapAbove='" + criteria.getMarketCapAbove() + 
		"' and marketCapBelow='" + criteria.getMarketCapBelow() + 
		"' and couponRateAbove='" + criteria.getCouponRateAbove() + 
		"' and couponRateBelow='" + criteria.getCouponRateBelow() + 
		"' and locationCode='" + criteria.getLocationCode()+"'");
		
		if(criteria.getVendor()!=null){
			queryString.append(" and vendor='" + criteria.getVendor()+"'");
		}else{
			queryString.append(" and vendor is null");
		}
		
		if(criteria.getInstrument()!=null){
			queryString.append(" and instrument='" + criteria.getInstrument()+"'");
		}else{
			queryString.append(" and instrument is null");
		}
		
		if(criteria.getScanCode()!=null){
			queryString.append(" and scanCode='" + criteria.getScanCode()+"'");
		}else{
			queryString.append(" and scanCode is null");
		}
		
		if(criteria.getMoodyRatingAbove()!=null){
			queryString.append(" and moodyRatingAbove='" + criteria.getMoodyRatingAbove()+"'");
		}else{
			queryString.append(" and moodyRatingAbove is null");
		}
		
		if(criteria.getMoodyRatingBelow()!=null){
			queryString.append(" and moodyRatingBelow='" + criteria.getMoodyRatingBelow()+"'");
		}else{
			queryString.append(" and moodyRatingBelow is null");
		}

		if(criteria.getSpRatingAbove()!=null){
			queryString.append(" and spRatingAbove='" + criteria.getSpRatingAbove()+"'");
		}else{
			queryString.append(" and spRatingAbove is null");
		}
		
		if(criteria.getSpRatingBelow()!=null){
			queryString.append(" and spRatingBelow='" + criteria.getSpRatingBelow()+"'");
		}else{
			queryString.append(" and spRatingBelow is null");
		}
		
		if(criteria.getMaturityDateAbove()!=null){
			queryString.append(" and maturityDateAbove='" + criteria.getMaturityDateAbove().toString()+"'");
		}else{
			queryString.append(" and maturityDateAbove is null");
		}
		
		if(criteria.getMaturityDateBelow()!=null){
			queryString.append(" and maturityDateBelow='" + criteria.getMaturityDateBelow().toString()+"'");
		}else{
			queryString.append(" and maturityDateBelow is null");
		}

		if(criteria.getExcludeConvertible()!=null){
			queryString.append(" and excludeConvertible='" + criteria.getExcludeConvertible()+"'");
		}else{
			queryString.append(" and excludeConvertible is null");
		}
		
		if(criteria.getScannerSettingPairs()!=null){
			queryString.append(" and scannerSettingPairs='" + criteria.getScannerSettingPairs()+"'");
		}else{
			queryString.append(" and scannerSettingPairs is null");
		}
		
		if(criteria.getStockTypeFilter()!=null){
			queryString.append(" and stockTypeFilter='" + criteria.getStockTypeFilter()+"'");
		}else{
			queryString.append(" and stockTypeFilter is null");
		}
		return queryString.toString();
	}
	
	@SuppressWarnings("unchecked")
	private ScanCriteria lookupIfNeeded(final ScanCriteria scanCriteria) {
		if(scanCriteria.hasId()) return scanCriteria; // not needed
		return (ScanCriteria)getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				String queryString=createScanCriteriaLookupQuery(scanCriteria);
				Query query = session.createQuery(queryString);
				List<ScanCriteria> list= query.list();
				if(list.size()==0){
					//log.warn("No scanCriteria for example found ...");
					return null; 
				}else if(list.size() > 1) {
					throw new IllegalArgumentException("ambiguity: scanCriteria matches more than one database entity: " + scanCriteria);
				}
				return list.get(0);
			}
		});
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public ScanCriteria findByExample(final ScanCriteria scanCriteria) {
		return lookupIfNeeded(scanCriteria);
	}
}